﻿using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using CID.Domain.Development;
using CID.Domain.Infrastructure;
using CID.Domain.Operations;
using CID.Domain.Users;
using CID.Web.Models.Infrastructure;
using CID.Web.Models.Operations;
using CID.Web.Models.Users;
using Newtonsoft.Json;

namespace CID.Web.Models.Development
{
    public class ComponentViewModel : BaseDescribedViewModel<IComponent>, IAjaxEnabledViewModel
    {
        #region Fields and Properties
        public string PostUrl { get; set; }

        public bool Enabled { get; set; }
        
        [Required(ErrorMessage = "You must provide a Required Server Role")]
        public int? RequiredServerRoleId { get; set; }
        [Display(Name = "Required Server Role")]
        public string RequiredServerRole { get; set; }

        [Required(ErrorMessage = "You must provide a Responsible")]
        public int? ResponsibleId { get; set; }
        public string Responsible { get; set; }

        [Required(ErrorMessage = "You must provide an Activation Template")]
        public int? ActivationTemplateId { get; set; }
        [Display(Name = "Activation Template")]
        public string ActivationTemplate { get; set; }

        [Required(ErrorMessage = "You must provide an Deactivation Template")]
        public int? DeactivationTemplateId { get; set; }
        [Display(Name = "Deactivation Template")]
        public string DeactivationTemplate { get; set; }

        public List<ParameterValueViewModel> ActivationParameterValues { get; set; }
        public List<ParameterValueViewModel> DeactivationParameterValues { get; set; }
        #endregion

        public ComponentViewModel() : base() 
        {
            ActivationParameterValues = new List<ParameterValueViewModel>();
            DeactivationParameterValues = new List<ParameterValueViewModel>();
        }
        
        //Required for Mocking purposes
        public ComponentViewModel(IComponent model) :this(model, new IEnvironment[0])
        {
        }
        public ComponentViewModel(IComponent model, params IEnvironment[] environments) : base(model)
        {
            ActivationParameterValues = new List<ParameterValueViewModel>();
            DeactivationParameterValues = new List<ParameterValueViewModel>();

            Enabled = model.Enabled;
            if (model.RequiredServerRole != null)
            {
                RequiredServerRole = model.RequiredServerRole.Name;
                RequiredServerRoleId = model.RequiredServerRole.Id;
            }

            if (model.Responsible != null)
            {
                Responsible = model.Responsible.Name;
                ResponsibleId = model.Responsible.Id;
            }

            if (model.ActivationDeactivationDefinition.ActivationTemplate != null)
            {
                ActivationTemplate = model.ActivationDeactivationDefinition.ActivationTemplate.Name;
                ActivationTemplateId = model.ActivationDeactivationDefinition.ActivationTemplate.Id;

                foreach (var parameterValue in model.ActivationDeactivationDefinition.DefaultParameterValues.Where(x => (x.Parameter.Template.Id == ActivationTemplateId)))
                    ActivationParameterValues.Add(new ParameterValueViewModel(parameterValue));
                foreach (var parameterValue in model.ActivationDeactivationDefinition.EnvironmentParameterValues.Where(x => (x.Parameter.Template.Id == ActivationTemplateId)))
                    ActivationParameterValues.Add(new ParameterValueViewModel(parameterValue));

                foreach (var environment in environments)
                    foreach (var parameter in model.ActivationDeactivationDefinition.ActivationTemplate.Parameters)
                        if (model.ActivationDeactivationDefinition.GetEnvironmentParameterValue(parameter, environment) == null)
                            ActivationParameterValues.Add(new ParameterValueViewModel(parameter, environment));
            }

            if (model.ActivationDeactivationDefinition.DeactivationTemplate != null)
            {
                DeactivationTemplate = model.ActivationDeactivationDefinition.DeactivationTemplate.Name;
                DeactivationTemplateId = model.ActivationDeactivationDefinition.DeactivationTemplate.Id;

                foreach (var parameterValue in model.ActivationDeactivationDefinition.DefaultParameterValues.Where(x => (x.Parameter.Template.Id == DeactivationTemplateId)))
                    DeactivationParameterValues.Add(new ParameterValueViewModel(parameterValue));
                foreach (var parameterValue in model.ActivationDeactivationDefinition.EnvironmentParameterValues.Where(x => (x.Parameter.Template.Id == DeactivationTemplateId)))
                    DeactivationParameterValues.Add(new ParameterValueViewModel(parameterValue));

                foreach (var environment in environments)
                    foreach (var parameter in model.ActivationDeactivationDefinition.DeactivationTemplate.Parameters)
                        if (model.ActivationDeactivationDefinition.GetEnvironmentParameterValue(parameter, environment) == null)
                            DeactivationParameterValues.Add(new ParameterValueViewModel(parameter, environment));
            }
        }

        public override void UpdateModel(IComponent model)
        {
            base.UpdateModel(model);

            var originalActivationTemplate = model.ActivationDeactivationDefinition.ActivationTemplate;
            var originalDeactivationTemplate = model.ActivationDeactivationDefinition.DeactivationTemplate;

            model.Enabled = Enabled;
            model.RequiredServerRole = availableServerRoles.SingleOrDefault(x => (x.Id == RequiredServerRoleId));
            model.Responsible = availableUsers.SingleOrDefault(x => (x.Id == ResponsibleId));
            model.ActivationDeactivationDefinition.ActivationTemplate = availableActivationTemplates.SingleOrDefault(x => (x.Id == ActivationTemplateId));
            model.ActivationDeactivationDefinition.DeactivationTemplate = availableDeactivationTemplates.SingleOrDefault(x => (x.Id == DeactivationTemplateId));

            #region ActivationParameterValues
            //Only push changes to the parameters if the activation template is not null and has not changed.
            if ((model.ActivationDeactivationDefinition.ActivationTemplate != null) && (model.ActivationDeactivationDefinition.ActivationTemplate == originalActivationTemplate))
            {
                foreach (var insertedParameterValue in InsertedActivationParameterValues)
                {
                    var parameterValue = model.ActivationDeactivationDefinition.AddEnvironmentParameterValue(
                        model.ActivationDeactivationDefinition.ActivationTemplate.Parameters.SingleOrDefault(x => x.Id == insertedParameterValue.ParameterId),
                        availableEnvironments.SingleOrDefault(x => x.Id == insertedParameterValue.EnvironmentId));
                    insertedParameterValue.UpdateModel(parameterValue);
                }

                foreach (var removedParameterValue in RemovedActivationParameterValues)
                {
                    var parameterValue = model.ActivationDeactivationDefinition.EnvironmentParameterValues.SingleOrDefault(x => x.Id == removedParameterValue.Id);
                    if (parameterValue != null)
                        model.ActivationDeactivationDefinition.RemoveEnvironmentParameterValue(parameterValue);
                }

                foreach (var modifiedParameterValue in ModifiedActivationParameterValues)
                {
                    IParameterValue parameterValue = model.ActivationDeactivationDefinition.DefaultParameterValues.SingleOrDefault(x => x.Id == modifiedParameterValue.Id);
                    if (parameterValue == null) parameterValue = model.ActivationDeactivationDefinition.EnvironmentParameterValues.SingleOrDefault(x => x.Id == modifiedParameterValue.Id);
                    if (parameterValue != null)
                        modifiedParameterValue.UpdateModel(parameterValue);
                }
            }
            #endregion

            #region DeactivationParameterValues
            //Only push changes to the parameters if the deactivation template is not null and has not changed.
            if ((model.ActivationDeactivationDefinition.DeactivationTemplate != null) && (model.ActivationDeactivationDefinition.DeactivationTemplate == originalDeactivationTemplate))
            {
                foreach (var insertedParameterValue in InsertedDeactivationParameterValues)
                {
                    var parameterValue = model.ActivationDeactivationDefinition.AddEnvironmentParameterValue(
                        model.ActivationDeactivationDefinition.DeactivationTemplate.Parameters.SingleOrDefault(x => x.Id == insertedParameterValue.ParameterId),
                        availableEnvironments.SingleOrDefault(x => x.Id == insertedParameterValue.EnvironmentId));
                    insertedParameterValue.UpdateModel(parameterValue);
                }

                foreach (var removedParameterValue in RemovedDeactivationParameterValues)
                {
                    var parameterValue = model.ActivationDeactivationDefinition.EnvironmentParameterValues.SingleOrDefault(x => x.Id == removedParameterValue.Id);
                    if (parameterValue != null)
                        model.ActivationDeactivationDefinition.RemoveEnvironmentParameterValue(parameterValue);
                }

                foreach (var modifiedParameterValue in ModifiedDeactivationParameterValues)
                {
                    IParameterValue parameterValue = model.ActivationDeactivationDefinition.DefaultParameterValues.SingleOrDefault(x => x.Id == modifiedParameterValue.Id);
                    if (parameterValue == null) parameterValue = model.ActivationDeactivationDefinition.EnvironmentParameterValues.SingleOrDefault(x => x.Id == modifiedParameterValue.Id);
                    if (parameterValue != null)
                        modifiedParameterValue.UpdateModel(parameterValue);
                }
            }
            #endregion
        }

        #region Edition helper members
        #region Users

        private IEnumerable<User> availableUsers;
    
        public void SetAvailableUsers(IEnumerable<User> availableUsers)
        {
            this.availableUsers = availableUsers;
        }

        [JsonIgnore]
        public IEnumerable<UserViewModel> AvailableUsers
        {
            get { return availableUsers.ToList().Select(x => new UserViewModel(x)); }
        }
        #endregion
        
        #region ServerRoles
        private IEnumerable<ServerRole> availableServerRoles;

        public void SetAvailableServerRoles(IEnumerable<ServerRole> availableServerRoles)
        {
            this.availableServerRoles = availableServerRoles;
        }
        
        [JsonIgnore]
        public IEnumerable<ServerRoleViewModel> AvailableServerRoles
        {
            get { return availableServerRoles.ToList().Select(x => new ServerRoleViewModel(x)); }
        }
        #endregion

        #region ActivationTemplates
        private IEnumerable<ActivationTemplate> availableActivationTemplates;

        public void SetAvailableActivationTemplates(IEnumerable<ActivationTemplate> availableActivationTemplates)
        {
            this.availableActivationTemplates = availableActivationTemplates;
        }
        
        [JsonIgnore]
        public IEnumerable<TemplateViewModel> AvailableActivationTemplates
        {
            get { return availableActivationTemplates.ToList().Select(x => new TemplateViewModel(x)); }
        }
        #endregion

        #region DeactivationTemplates
        private IEnumerable<DeactivationTemplate> availableDeactivationTemplates;

        public void SetAvailableDeactivationTemplates(IEnumerable<DeactivationTemplate> availableDeactivationTemplates)
        {
            this.availableDeactivationTemplates = availableDeactivationTemplates;
        }

        [JsonIgnore]
        public IEnumerable<TemplateViewModel> AvailableDeactivationTemplates
        {
            get { return availableDeactivationTemplates.ToList().Select(x => new TemplateViewModel(x)); }
        }
        #endregion

        #region Environments
        private IEnumerable<Environment> availableEnvironments;

        public void SetAvailableEnvironments(IEnumerable<Environment> availableEnvironments)
        {
            this.availableEnvironments = availableEnvironments;
        }
        #endregion
        #endregion

        #region ActivationParameterValues helpers
        private IEnumerable<ParameterValueViewModel> InsertedActivationParameterValues
        {
            get { return ActivationParameterValues.Where(x => (x.Id == 0) && x.HasValue); }
        }

        private IEnumerable<ParameterValueViewModel> RemovedActivationParameterValues
        {
            get { return ActivationParameterValues.Where(x => (x.Id != 0) && !x.HasValue); }
        }

        private IEnumerable<ParameterValueViewModel> ModifiedActivationParameterValues
        {
            get { return ActivationParameterValues.Where(x => (x.Id != 0) && x.HasValue); }
        }
        #endregion

        #region DeactivationParameterValues helpers
        private IEnumerable<ParameterValueViewModel> InsertedDeactivationParameterValues
        {
            get { return DeactivationParameterValues.Where(x => (x.Id == 0) && x.HasValue); }
        }

        private IEnumerable<ParameterValueViewModel> RemovedDeactivationParameterValues
        {
            get { return DeactivationParameterValues.Where(x => (x.Id != 0) && !x.HasValue); }
        }

        private IEnumerable<ParameterValueViewModel> ModifiedDeactivationParameterValues
        {
            get { return DeactivationParameterValues.Where(x => (x.Id != 0) && x.HasValue); }
        }
        #endregion
    }
}